﻿using System;
using System.Collections.Generic;

namespace Compiler.CompilerLogic
{
    [Serializable]
    public class SyntaxNode
    {
        public SyntaxNode ParentNode;
        public List<SyntaxNode> ChildNodes = new List<SyntaxNode>();

        public string NodeName;
        public string NodeVal;

        public SyntaxNode(string name,string val, SyntaxNode parent)
        {
            this.NodeName = name;
            this.NodeVal = val;
            this.ParentNode = parent;
        }

        public override bool Equals(object obj)
        {
            if (obj == null) return false;
            if (((SyntaxNode) obj).ChildNodes.Count != ChildNodes.Count) return false;
            for (int i = 0; i < ChildNodes.Count; i++)
            {
                if (!((SyntaxNode) obj).ChildNodes[i].Equals(ChildNodes[i]))
                {
                    return false;
                }
            }
            return ((SyntaxNode)obj).NodeName == NodeName && ((SyntaxNode)obj).NodeVal == NodeVal;
        }
    }

    [Serializable]
    public class SyntaxTree
    {
        private static SyntaxNode root;

        public static SyntaxNode GetRoot()
        {
            if (root == null)
            {
                root=new SyntaxNode("Program","",null);
            }
            return root;
        }
    }

   [Serializable]
    public abstract class Node // node of the AST
    {
       public static SyntaxNode CurrentNode;
        public abstract void dump();

       public void AddNode(string name, string val)
       {
           CurrentNode.ChildNodes.Add(new SyntaxNode(name, val, CurrentNode));
           CurrentNode = CurrentNode.ChildNodes[CurrentNode.ChildNodes.Count - 1];
       }
    }

    
    //----------- Declarations ----------------------------

    [Serializable]
    public class ObjT : Node // any declared object that has a name
    {
        public string name; // name of this object
        public int type; // type of this object (UNDEF for procedures)
        public ObjT(string s, int t)
        {
            name = s;
            type = t;
        }
        public override void dump()
        {
        }
    }

    [Serializable]
    public class Var : ObjT // variable
    {
        public int adr; // address in memory
        public Var(string name, int type)
            : base(name, type)
        {
        }
    }

    [Serializable]
    public class Proc : ObjT // procedure (also used for the main program)
    {
        public IList<ObjT> locals; // local objects declared in this procedures
        public Block block; // block of this procedure (null for the main program)
        public int nextAdr; // next free address in this procedure
        public Proc program; // link to the Proc node of the main program or null
        public Parser parser; // for error messages
        

        public Proc(string name, Proc program, Parser parser)
            : base(name, 0)
        {
            locals = new List<ObjT>();
            this.program = program;
            this.parser = parser;
            if (program == null)
            {
                CurrentNode = SyntaxTree.GetRoot();
            }
        }

        public virtual void add(ObjT obj)
        {
            foreach (ObjT o in locals)
            {
                if (o.name.Equals(obj.name))
                {
                    parser.SemErr(obj.name + " объявлен дважды");
                }
            }
            locals.Add(obj);
            if (obj is Var)
            {
                ((Var)obj).adr = nextAdr++;
            }
        }

        public virtual ObjT find(string name)
        {
            foreach (ObjT o in locals)
            {
                if (o.name.Equals(name))
                {
                    return o;
                }
            }
            if (program != null)
            {
                foreach (ObjT o in program.locals)
                {
                    if (o.name.Equals(name))
                    {
                        return o;
                    }
                }
            }
            parser.SemErr(name + " не объявлен");
            return new ObjT("_undef", 1);
        }

        public override void dump()
        {
            AddNode("Procedure",name);
            block.dump();
            CurrentNode = CurrentNode.ParentNode;
        }
    }

    //----------- Expressions ------------------------------

    [Serializable]
    public abstract class Expr : Node
    {
    }

    [Serializable]
    public class BinExpr : Expr
    {
        public Op op;
        public Expr left, right;
        public BinExpr(Expr e1, Op o, Expr e2)
        {
            op = o;
            left = e1;
            right = e2;
        }
        public override void dump()
        {
            AddNode("Expression",op.ToString());
            left.dump();
            right.dump();
            CurrentNode = CurrentNode.ParentNode;
        }
    }

    [Serializable]
    public class UnaryExpr : Expr
    {
        public Op op;
        public Expr e;
        public UnaryExpr(Op x, Expr y)
        {
            op = x;
            e = y;
        }
        public override void dump()
        {
            AddNode("Minus", "");
            e.dump();
            CurrentNode = CurrentNode.ParentNode;
        }
    }

    [Serializable]
    public class Ident : Expr
    {
        public ObjT obj;
        public Ident(ObjT o)
        {
            obj = o;
        }
        public override void dump()
        {
            AddNode("Ident", obj.name);
            CurrentNode = CurrentNode.ParentNode;
        }
    }

    [Serializable]
    public class IntCon : Expr
    {
        public int val;
        public IntCon(int x)
        {
            val = x;
        }
        public override void dump()
        {
            AddNode("Const",val.ToString());
            CurrentNode = CurrentNode.ParentNode;
        }
    }

    [Serializable]
    public class BoolCon : Expr
    {
        public bool val;
        public BoolCon(bool x)
        {
            val = x;
        }
        public override void dump()
        {
            AddNode("Const",val.ToString());
            CurrentNode = CurrentNode.ParentNode;
        }
    }

    //------------- Statements -----------------------------

    [Serializable]
    public class Stat : Node
    {
        public static int indent = 0;
        public override void dump()
        {

        }
    }

    [Serializable]
    public class Assignment : Stat
    {
        public ObjT left;
        public Expr right;
        public Assignment(ObjT o, Expr e)
        {
            left = o;
            right = e;
        }
        public override void dump()
        {
            base.dump();
            AddNode("Assign","");
            AddNode("Ident",left.name);
            CurrentNode = CurrentNode.ParentNode;
            right.dump();
            CurrentNode = CurrentNode.ParentNode;
        }
    }

    [Serializable]
    public class Call : Stat
    {
        public ObjT proc;
        public Call(ObjT o)
        {
            proc = o;
        }
        public override void dump()
        {
            base.dump();
            AddNode("Call",proc.name);
           CurrentNode = CurrentNode.ParentNode;;
        }
    }

    [Serializable]
    public class If : Stat
    {
        public Expr cond;
        public Stat stat;
        public If(Expr e, Stat s)
        {
            cond = e;
            stat = s;
        }
        public override void dump()
        {
            base.dump();
            AddNode("If","");
            cond.dump();
            Stat.indent++;
            stat.dump();
            Stat.indent--;
            CurrentNode = CurrentNode.ParentNode;
        }
    }

    [Serializable]
    public class IfElse : Stat
    {
        public Stat ifPart;
        public Stat elsePart;
        public IfElse(Stat i, Stat e)
        {
            ifPart = i;
            elsePart = e;
        }
        public override void dump()
        {
            AddNode("IfElseBlock","");
            ifPart.dump();
            base.dump();
            AddNode("Else","");
            Stat.indent++;
            elsePart.dump();
            Stat.indent--;
            CurrentNode = CurrentNode.ParentNode;
            CurrentNode = CurrentNode.ParentNode;
        }
    }

    [Serializable]
    public class While : Stat
    {
        public Expr cond;
        public Stat stat;
        public While(Expr e, Stat s)
        {
            cond = e;
            stat = s;
        }
        public override void dump()
        {
            base.dump();
            AddNode("While","");
            cond.dump();
            Stat.indent++;
            stat.dump();
            Stat.indent--;
            CurrentNode = CurrentNode.ParentNode;
        }
    }

    [Serializable]
    public class Block : Stat
    {
        public IList<Stat> stats = new List<Stat>();
        public virtual void add(Stat s)
        {
            stats.Add(s);
        }
        public override void dump()
        {
            base.dump();
            AddNode("Block","");
            Stat.indent++;
            foreach (Stat s in stats)
            {
                s.dump();
            }
            Stat.indent--;
            base.dump();
            CurrentNode = CurrentNode.ParentNode;
        }
    }
}
